// Copyright (c) Meta Platforms, Inc. and affiliates.

/* zs2_ctransform_legacy.h
 * These are older Transforms that are no longer used.
 * They remain supported for backward compatibility.
 * They can also serve as simple examples to learn how to write Transforms.
 **/

#ifndef ZSTRONG_ZS2_CTRANSFORM_LEGACY_H
#define ZSTRONG_ZS2_CTRANSFORM_LEGACY_H

#include <stddef.h>               // size_t
#include "openzl/zl_compressor.h" // ZL_Compressor
#include "openzl/zl_errors.h"     // ZL_Report
#include "openzl/zl_opaque_types.h"
#include "openzl/zl_portability.h" // ZL_NOEXCEPT_FUNC_PTR

#if defined(__cplusplus)
extern "C" {
#endif

/* ------------------------------------
 * Simplest single-stream pipe encoder
 * ------------------------------------
 *
 * This is the most trivial scenario,
 * where the custom encoder accepts 1 buffer of bytes as input,
 * and produces 1 buffer of bytes as output.
 *
 * ZL_PipeEncoderFn is a simple variant unable to query any parameter.
 *
 * The equivalent decompression variant is defined in zs2_dtransform_legacy.h .
 *
 * @CTid is a public ID used to match encoder and decoder sides.
 * Data generated by this custom transform
 * will be decoded by a custom decoder of same @CTid at decompression time.
 *
 * The encoder has a companion function @dstBound_f,
 * which tells Zstrong how much memory to allocate for the destination buffer.
 * @dstBound_f is optional, and can default to NULL.
 * In which case , it's assumed that dstCapacity == srcSize.
 * Note(@Cyan): current prototype doesn't allow @dstBound_f to fail.
 *
 * The amount of data generated into the destination buffer
 * must be provided as @return value.
 * The encoder does not have to use the whole destination buffer,
 * it can use less, however, it cannot use more.
 * Any @return value > dstCapacity will be interpreted as an error.
 * This, btw, provides a way to signal an encoding error,
 * though this API does not define any specific list of error codes.
 **/

typedef size_t (*ZL_PipeDstCapacityFn)(const void* src, size_t srcSize)
        ZL_NOEXCEPT_FUNC_PTR;
typedef size_t (*ZL_PipeEncoderFn)(
        void* dst,
        size_t dstCapacity,
        const void* src,
        size_t srcSize) ZL_NOEXCEPT_FUNC_PTR;

typedef struct {
    ZL_IDType CTid;
    ZL_PipeEncoderFn transform_f;
    ZL_PipeDstCapacityFn dstBound_f;
    const char* name; // Optional display name, for debugging purposes. Allowed
                      // to be NULL.
} ZL_PipeEncoderDesc;

/**
 * Register a custom pipe encoder, to be employed in upcoming Graph.
 * This registration is specialized for simple pipe transforms.
 * Counterpart: ZL_DCtx_registerPipeDecoder().
 * This action creates a NodeID, provided as @return value.
 * Graphs using custom encoders are only decodable if,
 * at decompression time, a custom decoder of same CTid is registered.
 **/
ZL_NodeID ZL_Compressor_registerPipeEncoder(
        ZL_Compressor* cgraph,
        const ZL_PipeEncoderDesc* ctd);

/* ------------------------------------
 * Split Transforms
 * ------------------------------------
 *
 * In this specialized transform scenario,
 * the custom encoder accepts 1 buffer of bytes as input,
 * and produces N buffers of bytes as output.
 * The nb N of outputs is static at declaration time.
 * Once declared, the transform *must* generate as many outputs as declared.
 *
 * Note : the corresponding decompression side does a reverse join operation.
 *
 * Split Transforms (and above) can query local and global parameters
 * via their @eic Encoder Interface state.
 *
 * @CTid is an ID that will be used to correlate encoder and decoder sides.
 * Data produced by this custom encoder
 * must be consumed by a custom decoder of same @CTid at decompression time.
 *
 * The encoder requests the creation of output buffers during its execution.
 * The encoder is in charge of determining
 * how many bytes are required for each output buffer.
 * It must also publish how many bytes are effectively used within each buffer.
 *
 * @return : the nb of output streams generated
 *          (necessarily == nb of output streams declared).
 *          An incorrect number will be interpreted as an error.
 *          More specific error codes can be provided using `zs2_error.h` API.
 *
 * ==> Important !!
 * The amount of data effectively written into each output buffer
 * **must** be provided by the encoder interface into @writtenSizes[].
 * Written content is always considered starting from beginning position 0.
 * The size of @writtenSizes[] array is consistent with transform's definition,
 * aka == @.nbOutputStreams .
 *
 * Design note : in this interface,
 * the nb of output streams is presumed already known by the function.
 * Therefore, this nb is not repeated in the parameter's list.
 **/

// Custom split-transform's signature
typedef ZL_Report (*ZL_SplitEncoderFn)(
        ZL_Encoder* eic,
        size_t writtenSizes[],
        const void* src,
        size_t srcSize) ZL_NOEXCEPT_FUNC_PTR;

// Declaration structure
typedef struct {
    ZL_IDType CTid;
    ZL_SplitEncoderFn transform_f;
    size_t nbOutputStreams; // must be > 0
    ZL_LocalParams localParams;
    const char* name;
} ZL_SplitEncoderDesc;

/**
 * Register a custom Split Transform
 */
ZL_NodeID ZL_Compressor_registerSplitEncoder(
        ZL_Compressor* cgraph,
        const ZL_SplitEncoderDesc* ctd);

// API Capabilities for Split Transforms

/* Request creation of all output buffers :
 *
 * ZL_Encoder_createAllOutBuffers() :
 * The encoder interface is in charge of requesting creation of output buffers.
 * This is an allocation operation, requested from the Encoder Interface,
 * but performed and registered by the Graph engine.
 *
 * This capability is specialized for custom split transforms.
 * It allocates all destination buffers in bulk,
 * requiring all destination buffer capacities to be decided upfront.
 * The encoder is not required to employ all the capacity.
 * But it is required to specify how much data was written into each buffer,
 * via its write param @writtenSizes.
 *
 * Requested buffer capacities are provided in @buffCapacities array,
 * and are expressed in nb of bytes.
 * The function provides writable pointers into allocated array @buffStarts,
 * indicating the start of each buffer.
 * The size of both @buffCapacities and @buffStarts arrays is @nbBuffs.
 *
 * The nb of requested buffers *must* be consistent with transform's definition.
 * i.e. @nbBuffs == @.nbOutputStreams .
 * If not, it's an error, and function will fail, returning an error code.
 *
 * buffStarts[] must be already allocated,
 * and sized to receive at least @nbBuffs pointers.
 *
 * Each @buffStarts pointer provides access to a writable memory area
 * at least as large as the requested capacity,
 * except if allocation failed. In which case, it returns a NULL pointer.
 * The caller should be prepared for such an outcome,
 * and test the return value of the function, which will indicate success.
 * Note that NULL is a valid pointer value if requested capacity == 0.
 *
 * ZL_Encoder_createAllOutBuffers() returns a ZL_Report,
 * which is either success, or an error (typically allocation_error).
 */
ZL_Report ZL_Encoder_createAllOutBuffers(
        ZL_Encoder* eic,
        void* buffStarts[],
        const size_t buffCapacities[],
        size_t nbBuffs);

#if defined(__cplusplus)
} // extern "C"
#endif

#endif // ZSTRONG_ZS2_CTRANSFORM_LEGACY_H
